package NetWork;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.SocketException;

//UDP版本的回显服务器 (接受请求,不会进行响应的计算,而是直接返回响应 (顾客点的菜,餐馆接受到后,直接把饭给顾客,省略了中间炒菜的过程))
//网络编程,本质上就是操作网卡,但是网卡不方便直接操作,在操作系统内核中,使用了一种特殊的 "socket" 文件来间接操作网卡
//进行网络通信,要先有一个socket对象
public class UdpEchoServer {
    private DatagramSocket socket;

    /** 对于服务器来说,创建socket对象的同时,要给他绑定一个具体的端口号
     *  服务器一定要关联一个具体的端口号
     *  服务器是网络传输中被动的一方,如果是操作系统随机分配的端口,此时客户端就不知道这个端口号是什么了,也就无法进行通信了
     */
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }
    public void start() throws IOException {
        System.out.println("服务器启动!!");
        //对于UDP来说,传输数据的基本单位是 DatagramPacket
        //对于服务器来说,是一天24小时的工作,所以可以死循环
        while (true) {
            //只要有客户端过来,就可以提供服务
            // 1.读取客户端发来的请求 (receive 参数是一个输出型参数,需要先构造好一个空白的DatagramPacket 对象,交给receive 来进行填充)
            // requestPacket 中有一个字节数组作为存储数据的缓冲区(给的长度是4096,但是这部分空间不一定全部用满了,可能只用一小部分)
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            //receive 内部会针对参数对象(requestPacket)填充数据 填充的数据来自于网卡
            //如果客户端发送了请求那么receive就会读取,如果一直没有收到请求,那么就会阻塞等待,知道接收到请求
            socket.receive(requestPacket);
            //此时这个 requestPacket 是一个特殊的对象,并不方便直接进行处理 可以把这里面得数据都拿出来,构造成一个字符串
            //通过getlength获取到实际的数据长度,只把这个实际有效部分构造成一个字符串即可
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            // 2.根据请求计算响应
            String response = process(request);
            // 3.返回响应到客户端, send的参数也是 DatagramPacket ,需要把这个Packet对象构造好
            // 此处构造的响应对象,就不能使用空的字节数组构造了,而是要根据响应数据来构造
            // 这里的长度不能写成response.length ()的原因是 responsePacket需要的是字节的个数,而不是字符的个数
            // requestPacket.getSocketAddress() 获取到客户端的ip和端口号,当receive数据后会得到一个ip地址和端口号(比如点餐,顾客通过receive
            // 将餐点完之后,老板给他们小票并且看到他们做的位置然后上菜就能知道他的ip地址和小票(端口号)给顾客)
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //4.打印这次请求,响应的处理中间结果
            System.out.printf("%s:%d req : %s resp : %s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),
                    request,response);
        }

    }
    //根据请求计算响应(因为是一个回显服务器,所以直接返回请求)
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        //端口号的指定,从1024 -> 65535当中随机挑选
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}
